#!/usr/bin/env python
# -*- coding: utf-8 -*-
# author: zl
import pymysql
import signal
import platform
import time
import threading
import traceback
from datetime import datetime, timedelta
from queue import Queue
from bigdata_imei.common.logger import Logger
# from common.logger import Logger
from enum import Enum

logger = Logger("schedule.log").getLogger()


class Status(Enum):
    # status: waiting、appending、running、success、fail
    Waiting = 'Waiting'
    Appending = 'Appending'
    Running = 'Running'
    Success = 'Success'
    Fail = 'Fail'


MAX_PROCESS_NUM = 30
G_RUN_FLAG = True
RUNNING_STEP = 60
QUEUE_SIZE = 1000

TIME_OUT = 8
BATCH_CNT = 50


class ConsumerExecute:
    def __init__(self, taskid, userid, policy_id_set):
        self.mysqlconn = Cursor.new(**FINANCE_DBINFO)
        self.taskid = taskid
        self.userid = userid

    def update_task_status(self, status=Status.Running,
                           mark='', rule_is_pass=0):
        sql = ''' update fn_policy_user_task set status='{1}', mark='{2}', rule_is_pass='{3}' WHERE id={0} '''.format(
            self.taskid, status, mark, rule_is_pass)
        self.mysqlconn.execute(sql)

    def run_task(self):
        self.update_task_status()
        self.judge_policy_set()


class ProducerExecute:
    def __init__(self):
        self.db = pymysql.Connect(**FINANCE_TS_DBINFO)
        self.cursor = self.db.cursor()
        self.mysql_select_cnt = 0

    def available_task_list(self):
        tasks = []
        try:
            nowtime = (datetime.now() - timedelta(minutes=TIME_OUT)
                       ).strftime('%Y-%m-%d %H:%M:%S')
            if self.mysql_select_cnt % 2 == 0:
                select_sql = """ select id, userid, policyids from fn_policy_user_task where status='{0}' order by id limit {1} """.format(
                    Status.Waiting, BATCH_CNT)
            else:
                select_sql = """ select id, userid, policyids from fn_policy_user_task where utime<'{0}' and status IN ('{1}', '{2}') limit {3} """.format(
                    nowtime, Status.Appending, Status.Running, BATCH_CNT)

            self.cursor.execute(select_sql)
            results = self.cursor.fetchall()

            for row in results:
                id = int(row[0])
                userid = int(row[1])
                policyid = row[2]
                tasks.append((id, userid, policyid))

            self.mysql_select_cnt += 1

            if len(tasks) == 0:
                self.db.commit()
                return tasks
            elif len(tasks) == 1:
                ids = '(' + str(tasks[0][0]) + ')'
            else:
                ids = str(tuple(map(lambda x: x[0], tasks)))

            update_sql = ''' update fn_policy_user_task set status='{0}' where id in {1} '''.format(
                Status.Appending, ids)
            # print update_sql
            self.cursor.execute(update_sql)
            self.db.commit()

            if self.mysql_select_cnt >= 1000:
                self.mysql_select_cnt = 0
        except BaseException:
            self.db.rollback()
            logger.error(traceback.format_exc())
        return tasks


def main():
    queue = Queue(QUEUE_SIZE)
    producer = Producer(queue, RUNNING_STEP)
    producer.start()

    for i in range(MAX_PROCESS_NUM):
        consumer = Consumer(queue)
        consumer.setDaemon(True)
        consumer.start()

    producer.join()


def signal_runflag(sig, frame):
    global G_RUN_FLAG
    if sig == signal.SIGHUP:
        logger.info("receive HUP signal ")
        G_RUN_FLAG = False


class Producer(threading.Thread):
    def __init__(self, queue, step):
        threading.Thread.__init__(self)
        self.queue = queue
        self.step = step

    def run(self):
        self.execute = ProducerExecute()
        while G_RUN_FLAG:
            try:
                for task in self.execute.available_task_list():
                    if self.queue.qsize() >= QUEUE_SIZE:
                        time.sleep(self.step)
                    self.queue.put(task)
                logger.info('queueed tasks:' + str(self.queue.qsize()))
                time.sleep(int(self.step / 6))
            except BaseException:
                logger.error(traceback.format_exc())


class Consumer(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        while G_RUN_FLAG:
            try:
                task = self.queue.get()
                execute = ConsumerExecute(task)
                execute.run_task()
            except BaseException:
                logger.error(traceback.format_exc())


if __name__ == "__main__":
    logger.info("execute run")
    if platform.system() == "Linux":
        signal.signal(signal.SIGHUP, signal_runflag)
    main()
    logger.info("execute exit.")
